// **************************************************************************
// reversing by KVaks
// **************************************************************************
#include <libdma.h>

#define DMA_CHAN_COUNT      10

sceDmaChan* dch[DMA_CHAN_COUNT];
int         isclr[DMA_CHAN_COUNT];
int         sceDmaDebugMode = 0;

u_char      ststbl[DMA_CHAN_COUNT];
u_char      stdtbl[DMA_CHAN_COUNT];
u_char      mfdtbl[DMA_CHAN_COUNT];

sceDmaEnv   sceDmaCurrentEnv;


// **************************************************************************
// memclr
// **************************************************************************
void memclr(char* buffer,int count)
{
  while (count-->0) *buffer++=0;
}

// **************************************************************************
// sceDmaGetChan
// -------------
// Description:
//   Returns the address of the sceDmaChan structure
//   corresponding to the channel number id.
// Return value :
//   The address of the sceDmaChan structure corresponding
//   to the channel number id is returned. If the id
//   value is invalid, an error occurs, and 0 is returned.
// **************************************************************************
sceDmaChan* sceDmaGetChan(int id)
{
  if (id>=DMA_CHAN_COUNT) return NULL;

  return dch[id];
}

// **************************************************************************
// sceDmaReset
// -----------
// Description:
//   Resets the DMAC.
//   After waiting for termination of all DMA transfers, this function
//   clears all end-of-transfer handlers, initializes
//   the DMAC, and enables or disables the DMAC according to the mode.
// Return Value:
//   Previous mode value
// **************************************************************************
int sceDmaReset(int mode)
{
  int       prevmode = *D_CTRL & D_CTRL_DMAE_M;
  sceDmaEnv dmaEnv;

  for (int i = 0;i<DMA_CHAN_COUNT;i++) {
     if (isclr[i]==0) continue;

     dch[i]->chcr = 0;
     dch[i]->madr = NULL;
     dch[i]->tadr = NULL;
     dch[i]->as0  = NULL;
     dch[i]->as1  = NULL;
     dch[i]->sadr = NULL;
  }

  *D_STAT = 0xFF1F;
  *D_STAT = (*D_STAT & 0xFF1F0000);

  memclr(&dmaEnv,sizeof(dmaEnv));
  sceDmaPutEnv(&dmaEnv);

  if (mode!=1) return prevmode;

  *D_CTRL = *D_CTRL | D_CTRL_DMAE_M;

  return prevmode;

}

// **************************************************************************
// sceDmaDebug
// -----------
// Description :
//   Sets the debugging mode. If 1 is specified, debugging is turned on
//   and an argument consistency check will be performed by each
//   libdma function.
// Return value :
//   Previous mode value
// **************************************************************************
int sceDmaDebug(int mode)
{
  int oldmode = sceDmaDebugMode;
  sceDmaDebugMode = mode;
  return oldmode;
}

// **************************************************************************
// sceDmaPutEnv
// ------------
// Description:
//   Sets the DMAC registers with the contents of the structure
//   pointed to by env.
// Return value :
//   On normal termination, 0 is returned.
//   If the contents of the structure pointed to by env are invalid, a
//   negative number is returned.
// **************************************************************************
int sceDmaPutEnv(sceDmaEnv* env)
{
  u_int  d_ctrl = *D_CTRL;

  if (env->sts   >=DMA_CHAN_COUNT) return -1;
  if (env->std   >=DMA_CHAN_COUNT) return -2;
  if (env->mfd   >=DMA_CHAN_COUNT) return -3;
  if (env->rcycle>=7             ) return -4;

  d_ctrl = (d_ctrl & ~D_CTRL_STS_M) | (ststbl[env->sts] << 4);
  d_ctrl = (d_ctrl & ~D_CTRL_STD_M) | (stdtbl[env->std] << 6);
  d_ctrl = (d_ctrl & ~D_CTRL_MFD_M) | (mfdtbl[env->mfd] << 2);

  if (env->rcycle!=0) {
    d_ctrl=d_ctrl | D_CTRL_RELE_M;
    d_ctrl = (d_ctrl & (~D_CTRL_RCYC_M)) | ((env->rcycle-1)<<8);
  } else d_ctrl=d_ctrl & (~D_CTRL_RELE_M);

  *D_CTRL = d_ctrl;

  t0 = env->rbmsk

  *D_PCR  = (env->express<<16) | env->notify;
  *D_SQWC = (env->tqwc   <<16) | env->sqwc;
  *D_RBOR = (env->rbadr      );
  *D_RBSR = (env->rbmsk      );

  sceDmaCurrentEnv = *env;

  return 0;
}

// **************************************************************************
// sceDmaGetEnv
// ------------
// Description:
//   Reads the values of the DMAC common channel registers and stores
//   them in the structure specified by denv.
// Return value:
//   denv is returned.
// **************************************************************************
sceDmaEnv* sceDmaGetEnv(sceDmaEnv *denv)
{
  *denv = sceDmaCurrentEnv;
  return denv;
}

// **************************************************************************
// sceDmaPutStallAddr
// ------------------
// Desription:
//   Sets the value of addr in the D_STADR register,
//   which indicates the DMA stall address.
// Return value:
//   The previous stall address is returned.
// **************************************************************************
void *sceDmaPutStallAddr(void *addr)
{
  void* prevstall_addr = (void*)*D_STADDR;

  if (addr != 0xFFFFFFFF) *D_STADDR = (u_int)addr;

  return prevstall_addr;
}

// **************************************************************************
// sceDmaSend
// ----------
// Parameters:
//   d   - DMA channel for performing transfer
//   tag - Starting address of transfer list  
// Description:
//   Starts DMA transfer from memory to a device using Source Chain Mode.
//   DMAtag, which is the beginning of the data to be transferred,
//   is specified by tag, and the destination device (DMA channel) is
//   specified by d.
// **************************************************************************
void sceDmaSend(sceDmaChan *d,sceDmatag *tag)
{
  int timeout = 0x01000000;

  while (d->chcr.STR==1) {
    if (timeout<0) {
      printf("libdma: sync timeout\n");
      if (d->chcr.STR==1) d->chcr.STR=(d->chcr.STR & 0xFEFF);
    }
    timeout--;
  }

  setADR (d->tadr,tag);
  setQWC (d->qwc ,  0);
  setCHCR(d->chcr,1,1);

}

// **************************************************************************
// sceDmaSendN
// ----------
// Parameters:
//   d    - DMA channel for performing transfer
//   addr - Transfer starting address
//   size - Size of data to be transferred (qword)
// Description:
//   Starts DMA transfer from memory to a device using Normal Mode.
//   The starting address of the transfer
//   data is specified by addr, and the data size is specified by size.
// **************************************************************************
void sceDmaSendN(sceDmaChan *d,void* addr,int size)
{
  int timeout = 0x01000000;

  while (d->chcr.STR==1) {
    if (timeout<0) {
      printf("libdma: sync timeout\n");
      if (d->chcr.STR==1) d->chcr.STR=(d->chcr.STR & 0xFEFF);
    }
    timeout--;
  }

  if (d->madr!=(void *)-1) d->madr=addr;
  setQWC (d->qwc ,size);
  setCHCR(d->chcr,0,1 );

}

// **************************************************************************
// sceDmaSendI
// ----------
// Parameters:
//   d    - DMA channel for performing transfer
//   addr - Transfer starting address
//   size - Size of data to be transferred (qword)
// Description:
//   Starts DMA transfer from memory to SPR using Interleave Mode.
//   The channel structure address of the
//   SPR channel (id=9) must be specified for d.
// **************************************************************************
void sceDmaSendI(sceDmaChan *d,void* addr,int size)
{
  int timeout = 0x01000000;

  while (d->chcr.STR==1) {
    if (timeout<0) {
      printf("libdma: sync timeout\n");
      if (d->chcr.STR==1) d->chcr.STR=(d->chcr.STR & 0xFEFF);
    }
    timeout--;
  }

  if (d->madr!=(void *)-1) d->madr=addr;
  setQWC (d->qwc ,size);
  setCHCR(d->chcr,2,1 );

}

// **************************************************************************
// sceDmaRecv
// ----------
// Parameters:
//   d   - DMA channel for performing transfer
// Description:
//   Starts DMA transfer from a device to memory using Destination Chain Mode.
// **************************************************************************
void sceDmaRecv(sceDmaChan *d)
{
  int timeout = 0x01000000;

  while (d->chcr.STR==1) {
    if (timeout<0) {
      printf("libdma: sync timeout\n");
      if (d->chcr.STR==1) d->chcr.STR=(d->chcr.STR & 0xFEFF);
    }
    timeout--;
  }

  setQWC (d->qwc ,  0);
  setCHCR(d->chcr,1,0);

}

// **************************************************************************
// sceDmaRecvN
// ----------
// Parameters:
//   d    - DMA channel for performing transfer
//   addr - Memory address of transfer destination
//   size - Size of data to be transferred (qword)
// Description:
//   Starts DMA transfer from a device to memory using Normal Mode. \
//   The memory address that is the transfer destination is specified by addr,
//   and the size of the data to be transferred is specified by size.
// **************************************************************************
void sceDmaRecvN(sceDmaChan *d,void* addr,int size)
{
  int timeout = 0x01000000;

  while (d->chcr.STR==1) {
    if (timeout<0) {
      printf("libdma: sync timeout\n");
      if (d->chcr.STR==1) d->chcr.STR=(d->chcr.STR & 0xFEFF);
    }
    timeout--;
  }

  if (d->madr!=(void *)-1) d->madr=addr;
  setQWC (d->qwc ,size);
  setCHCR(d->chcr,0,0 );

}

// **************************************************************************
// sceDmaRecvI
// ----------
// Parameters:
//   d    - DMA channel for performing transfer (fromSPR)
//   addr - Memory address of transfer destination
//   size - Size of data to be transferred (qword)
// Description:
//   Starts DMA transfer from SPR to memory using Interleave Mode.
//   fromSPR (id=8) must be specified for d.
// **************************************************************************
void sceDmaRecvI(sceDmaChan *d,void* addr,int size)
{
  int timeout = 0x01000000;

  while (d->chcr.STR==1) {
    if (timeout<0) {
      printf("libdma: sync timeout\n");
      if (d->chcr.STR==1) d->chcr.STR=(d->chcr.STR & 0xFEFF);
    }
    timeout--;
  }

  if (d->madr!=(void *)-1) d->madr=addr;
  setQWC (d->qwc ,size);
  setCHCR(d->chcr,2,0 );

}

// **************************************************************************
// sceDmaSendI
// ----------
// Parameters:
//   saddr - SPR address of transfer source
//   size  - Size of data to be transferred (qword)
// Description:
//   Starts DMA transfer from SPR to MFIFO. Parameters indicating the
//   transfer destination MFIFO must be set in advance for the DMAC
//   via rbadr and rbmsk of the sceDmaEnv structure.
// **************************************************************************
void sceDmaSendM(void *saddr,int size)
{
  printf("DSendM: not installed yet,sorry\n");
}

// **************************************************************************
// sceDmaSync
// ----------
// Parameters:
//   d       - Relevant DMA channel
//   mode    - Block mode (0 : Block) (1 : Non-block)
//   timeout - Maximum wait interval
// Description:
//   Detects the end of DMA transfer for the channel specified by d.
// Return value:
//   If the DMA transfer is in process, 1 is returned.
//   If the DMA transfer has terminated, 0 is returned.
// **************************************************************************
int sceDmaSync(sceDmaChan *d, int mode,int timeout)
{
{
  if (mode==1) return d->chcr.STR;

  if (timeout==0) timeout=0x01000000;

  while (d->chcr.STR==1) {
    if (timeout<0) {
      printf("libdma: sync timeout\n");
      if (d->chcr.STR==1) d->chcr.STR=(d->chcr.STR & 0xFEFF);
    }
    timeout--;
  }

}

// **************************************************************************
// sceDmaWatch
// ----------
// Parameters:
//   d       - Relevant DMA channel
//   addr    - Memory address to be checked
//   mode    - Block mode (0 : Block) (1 : Non-block)
//   timeout - Maximum wait interval
// Description:
//   Checks whether or not the data at address addr has been
//   transferred using the channel specified by d.
// Return value:
//   If the address has been transferred, 1 is returned.
//   Otherwise, 0 is returned.
// **************************************************************************
int sceDmaWatch(sceDmaChan *d,void *addr,int mode,int timeout)
{
  if (mode==0) return (d->maddr<addr);

  while (d->chcr.STR==1) {
    if (timeout<0) {
      printf("libdma: sync timeout\n");
      if (d->chcr.STR==1) d->chcr.STR=(d->chcr.STR & 0xFEFF);
    }
    timeout--;
  }

  return 0;
}


int sceDmaSyncN(int mode,int timeout)
{
  printf("DSyncN: not installed yet,sorry\n");
}

void sceDmaLastSyncTime(int time[16])
{
  printf("DLastSyncTime: not installed yet,sorry\n");
}

// **************************************************************************
// sceDmaPause
// -------------
// Parameters:
//   d - DMA channel for restarting transfer
// Description
//   Restarts transfer processing for the specified DMA channel.
// Return value:
//   If the transfer was stopped, 0 is returned.
//  If it was already operating, 1 is returned.
// **************************************************************************
int sceDmaPause(sceDmaChan *d)
{
  int transfer_mode = d->chcr.STR;

  d->chcr.STR = 0;

  return transfer_mode;
}

// **************************************************************************
// sceDmaRestart
// -------------
// Parameters:
//   d - DMA channel for restarting transfer
// Description
//   Restarts transfer processing for the specified DMA channel.
// Return value:
//   If the transfer was stopped, 0 is returned.
//  If it was already operating, 1 is returned.
// **************************************************************************
int sceDmaRestart(sceDmaChan *d)
{
  int transfer_mode = d->chcr.STR;

  d->chcr.STR = 1;

  return transfer_mode;
}



void *sceDmaCallback(int cause,void (*func)())
{
  printf("DCallback: not installed yet,sorry\n");
}
